package com.jt.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component      //将AOP类加入spring容器
//@Aspect         //2.标识该类为AOP切面
//  Spring容器默认不能识别切面注解,需要手动配置
public class AOP {
    /**
     * 切入点表达式练习
     * within:
     *  1.within(com.jt.*.DeptServiceImpl)   一级包下的类
     *  2.within(com.jt..*.DeptServiceImpl)  ..代表多级包下的类
     *  3.within(com.jt..*)  包下的所有的类
     *
     * execution(返回值类型 包名.类名.方法名(参数列表))
     *  1.execution(* com.jt..*.DeptServiceImpl.add*())
     *  注释: 返回值类型任意的, com.jt下的所有包中的DeptServiceImpl的类
     *        的add开头的方法 ,并且没有参数.
     *
     *  2.execution(* com.jt..*.*(..))
     *  注释: 返回值类型任意,com.jt包下的所有包的所有类
     *        的所有方法 任意参数.
     *
     *  3.execution(int com.jt..*.*(int))
     *  4.execution(Integer com.jt..*.*(Integer))
     *  强调: 在Spring表达式中没有自动拆装箱功能! 注意参数类型
     *
     * 注解形式: @annotation(包名.注解名)
     *  @Before("@annotation(com.jt.anno.Cache)")
     */
//    @Before("bean(deptServiceImpl)")
//    @Before("within(com.jt.service.DeptServiceImpl)")
//    @Before("execution(* com.jt..*.add*(..))")  //返回值任意，com.jt下所有包的add开头的方法的所有参数
    @Pointcut("@annotation(com.jt.anno.Cache)")
    public void pointcut(){
        System.out.println("我是切入点表达式");
    }

    /**
     * 监控目标方法的状态
     * @param joinPoint
     */
    @Before("@annotation(com.jt.anno.Cache)")
    public void before(JoinPoint joinPoint){
        System.out.println("获取被代理的对象："+joinPoint.getTarget().getClass());
        System.out.println("获取目标对象类名："+joinPoint.getSignature().getDeclaringTypeName());
        System.out.println("获取目标对象方法名："+joinPoint.getSignature().getName());
        System.out.println("获取目标对象参数："+ Arrays.toString(joinPoint.getArgs()));
        System.out.println("before通知");
    }

    /**
     * 监控记录方法的返回值
     *
     */
    @AfterReturning(value = "pointcut()",returning = "result")
    public void AfterReturning(Object result){  //关于error at ::0 formal unbound in pointcut
                                                //当JoinPoint和返回值共用时，连接点对象必须位于第一位
                                                //Spring在进行参数赋值时，采用index[0]赋值
                                                //报错提示  ::0
        System.out.println("afterReturning通知");
        System.out.println("用户的返回值结果："+result);
    }

    /**
     * 记录报错信息
     * @param e
     */
    @AfterThrowing(pointcut = "pointcut()",throwing = "e")
    public void AfterThrowing(Exception e){
        System.out.println("异常信息："+e.getMessage());
        System.out.println("异常类型："+e.getClass());
    }

    @After("pointcut()")
    public void After(){
        System.out.println("after通知");
    }

    @Around("pointcut()")
    public Object Around(ProceedingJoinPoint proceedingJoinPoint){
        Object result = null;
        try {
            System.out.println("环绕通知开始");
            long startTime = System.currentTimeMillis();

            result = proceedingJoinPoint.proceed();  //目标方法执行
//            Thread.sleep(1000);
            long endTime = System.currentTimeMillis();
            System.out.println("程序执行时长："+(endTime-startTime));
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return result;
    }
}
